#include "ConnectionPool.h"
#include "simpleini/SimpleIni.h"

#include <functional>
#include <iostream>
#include <thread>

//#define DEBUG_CONNECTIONPOOL

constexpr uint32_t INIT_SIZE = 10;              // 初始数据库连接数量
constexpr uint32_t MAX_SIZE = 100;              // 最大数据库连接数量
constexpr uint32_t MAX_IDLE_TIME = 60;          // 连接最大空闲时间,单位:秒
constexpr uint32_t CONNECTION_TIMEOUT = 100;    // 获取连接超时时间,单位:毫秒

ConnectionPool* ConnectionPool::getConnectionPool()
{
    static ConnectionPool pool;
    return &pool;
}

ConnectionPool::ConnectionPool()
    : running_(true)
    , initSize_(INIT_SIZE)
    , maxSize_(MAX_SIZE)
    , currentSize_(0)
    , maxIdleTime_(MAX_IDLE_TIME)
    , connectionTimeout_(CONNECTION_TIMEOUT)
{
    // 加载配置文件
    std::string name = "../config.ini";
    if (!loadConfigFile(name))
    {
        throw std::runtime_error("load config file failed");
    }

    // 初始化连接池连接
    if (!initConnection())
    {
        throw std::runtime_error("produce initSize connecetion failed");
    }

    // 启动生产者线程
    std::thread t1(std::bind(&ConnectionPool::produceConnection, this));
    t1.detach();

    // 启动检测空闲连接线程
    std::thread t2(std::bind(&ConnectionPool::checkIdleConnection, this));
    t2.detach();
}

bool ConnectionPool::loadConfigFile(std::string name)
{
    CSimpleIniCaseA ini;
    ini.SetUnicode();   // 使用UTF-8编码

    SI_Error rc = ini.LoadFile(name.c_str());
    if (rc < 0)
    {
        std::cerr << "load file(" << name << ") failed: " << rc << std::endl;
        return false;
    }

    // 读取配置文件
    ip_         = ini.GetValue("MYSQL_INI", "ip");
    port_       = std::atoi(ini.GetValue("MYSQL_INI", "port"));
    password_   = ini.GetValue("MYSQL_INI", "password");
    user_       = ini.GetValue("MYSQL_INI", "user");
    dbname_     = ini.GetValue("MYSQL_INI", "dbname");
#ifdef DEBUG_CONNECTIONPOOL
    std::cout << "ip: " << ip_ << ", " << "port: " << port_ << ", "
              << "password: " << password_ << ", " << "user: " << user_ << ", "
              <<  "dbname: " << dbname_ << std::endl;
#endif
    return true;
}

bool ConnectionPool::initConnection()
{
    for (uint32_t i = 0; i < initSize_; ++i)
    {
        auto p = new Connection();
        if (p->connect(ip_, port_, user_, password_, dbname_))
        {
            p->updateLastOperationTime();
            connQue_.push(p);
            currentSize_++;
#ifdef DEBUG_CONNECTIONPOOL
            std::cout << "初始生产连接: " << (void*)p << ", 当前连接总数: " << currentSize_ << std::endl;
#endif
        }
        else
        {
            return false;
        }
    }
    return true;
}

ConnectionPool::~ConnectionPool()
{
    std::lock_guard<std::mutex> lock(mtx_);
    running_ = false;
    while (!connQue_.empty())
    {
        auto p = connQue_.front();
        connQue_.pop();
        delete p;
        p = nullptr;
    }
    cond_.notify_all(); // 通知生产者线程退出
}

std::shared_ptr<Connection> ConnectionPool::getConnection()
{
    std::unique_lock<std::mutex> lock(mtx_);

    // 若连接池队列为空，等待获取数据库连接（直到到达获取连接超时时间还没获取到）
    bool status = cond_.wait_for(lock, std::chrono::milliseconds(connectionTimeout_), 
        [&] { return !connQue_.empty(); }
    );

    // 超时，未获取到数据库连接，通知生产者线程生产连接，返回nullptr
    if (!status)
    {
        cond_.notify_all();
        return nullptr;
    }

    // 获取连接
    // 智能指针（自定义删除器，将连接归还到连接池中，而不是释放）
    std::shared_ptr<Connection> p(connQue_.front(),
        [&](Connection* pConn) {
#ifdef DEBUG_CONNECTIONPOOL
            std::cout << "将连接归还至连接池: " << (void*)p.get() << std::endl;
#endif
            std::lock_guard<std::mutex> lock(mtx_);
            pConn->updateLastOperationTime(); // 更新连接最后一次操作时间
            connQue_.push(pConn);
        }
    );
    connQue_.pop();
    cond_.notify_all();     // 通知生产者线程，检查一下，如果连接池为空，就生产连接

#ifdef DEBUG_CONNECTIONPOOL
    std::cout << "从连接池中获取到连接: " << (void*)p.get() << std::endl;
#endif
    return p;
}

void ConnectionPool::produceConnection()
{
    while (running_)
    {
        std::unique_lock<std::mutex> lock(mtx_);

        // 等待消费者通知，直到连接池队列为空
        cond_.wait(lock, [&]() { return connQue_.empty() || !running_; });

        if (!running_) return;

        // 执行到此处了，说明当前连接池队列为空，要生产连接了
    
        // 已经到达最大连接数量，不能再生产连接了
        if (currentSize_ >= maxSize_) continue;

        // 生产连接
        auto p = new Connection();
        if (p->connect(ip_, port_, user_, password_, dbname_))
        {
            p->updateLastOperationTime();
            connQue_.push(p);
            currentSize_++;
#ifdef DEBUG_CONNECTIONPOOL
            std::cout << "生产连接: " << (void*)p << ", 当前连接总数: " << currentSize_ << std::endl;
#endif
            cond_.notify_all(); // 通知消费者
        }
    }
}

void ConnectionPool::checkIdleConnection()
{
    while (running_)
    {
        // 通过sleep模拟定时效果
        std::this_thread::sleep_for(std::chrono::seconds(maxIdleTime_));

        std::lock_guard<std::mutex> lock(mtx_); // 获取锁

        if (!running_) return ;

        while (currentSize_ > initSize_)
        {
            auto p = connQue_.front();
            if (p->getIdleTimeMillis() >= (maxIdleTime_ * 1000))
            {
                connQue_.pop();
                currentSize_--;
#ifdef DEBUG_CONNECTIONPOOL
                std::cout << "空闲连接释放: " << (void*)p << ", 当前连接总数: " << currentSize_ << std::endl;
#endif
                delete p;
                p = nullptr;
            }
            else
            {
                break;  // 队头的连接都没有超过maxIdleTime_，其他连接肯定没有
            }
        }
    }
}
